﻿#include "AnimationSettingData.h"
#include "InfomationGraph3DBasical.h"
#include "FieldGraphGenerator.h"
#include "Graph3DWindowBase.h"
#include "PropPickerCallbackHander.h"
#include "pstInteractorStyle.h"
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo >
#include <QHBoxLayout>
#include <QObject>
#include <QSplitter>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkAxesActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkImageResize.h>
#include <vtkJPEGWriter.h>
#include <vtkLightKit.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkOutputWindow.h>
#include <vtkProp.h>
#include <vtkPropPicker.h>
#include <vtkReaderAlgorithm.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <vtkScalarBarActor.h>
#include <vtkUnstructuredGrid.h>
#include <vtkWindowToImageFilter.h>
#include <vtkXMLUnstructuredGridReader.h>

#include <IFSelect_PrintCount.hxx>
#include <IFSelect_ReturnStatus.hxx>
#include <IVtkOCC_ShapeMesher.hxx>
#include <IVtkTools_DisplayModeFilter.hxx>
#include <IVtkTools_ShapeDataSource.hxx>
#include <IVtkVTK_ShapeData.hxx>
#include <Standard_Integer.hxx>
#include <STEPControl_Reader.hxx>
#include <TopoDS_Shape.hxx>
#include <vtkPolyDataMapper.h>
#include <vtkPolyDataNormals.h>
#include <vtkDistanceToCamera.h>
#include <vtkGlyph3D.h>
#include <vtkNamedColors.h>
#include <vtkSphereSource.h>
#include <vtkTexture.h>
#include <vtkTextProperty.h>
#include <vtkBillboardTextActor3D.h>
#include <vtkCaptionActor2D.h>
#include <vtkTextActor.h>
#include <vtkExtractCells.h>
#include <vtkFeatureEdges.h>
#include <vtkProperty2D.h>
#include <vtkCubeAxesActor.h>
#include <vtkCellData.h>

#include "CommonFunction.h"
#include "farFieldDataGenerator.h"
#include "PostMarkerInteractionStyle.h"
#include "FramelessDragableTableWidget.h"
#include "DataClipperGenerator.h"
#include "BaseWidget/IdaDialogMsg.h"
#include "GeomtryActorGenerator.h"
#include "AnimationTooBarInfoData.h"

#include <vtkPlaneSource.h>
#include <vtkProperty.h>
#include <vector>
#include <array>
#include <cmath>
#include <algorithm>
#include "MainWindowSignalHandler.h"
#include "BaseModule/IBaseSignal.h"
#include "BaseWidget/IWidgetSignal.h"

#include "DataManager/WindowTotalData.h"

namespace pst
{
    Graph3DWindowBase::Graph3DWindowBase(QWidget* parent)
        : QWidget(parent)
        , m_qvtkWidget(new QVTKOpenGLNativeWidget(this))
        , m_renderWindow3D(vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New())
        //, m_fieldGraphGenerator(std::shared_ptr<pst::FieldGraphGenerator>(new pst::FieldGraphGenerator()))
        , m_render3D(vtkSmartPointer<vtkRenderer>::New())
        , m_leftDownCornerAxes(vtkSmartPointer<vtkAxesActor>::New())
        , m_leftDownCornerWidget(vtkSmartPointer<vtkOrientationMarkerWidget>::New())
        , m_camera(m_render3D->GetActiveCamera())
        , m_animationType{ 1 } // 默认相机
        , m_animationSpeed{ 1. }
        //, m_animationDataList{}
        , m_animationSettingData(new AnimationSettingData(this))
        , m_dataTimer(new QTimer(this))
        , m_cameraTimer(new QTimer(this))
        , m_isExportingAnimation{ false }
        , m_loopCount{ 0 }
        , m_windowToImageFilter(vtkSmartPointer<vtkWindowToImageFilter>::New())
        , m_animationImageList{}
        , m_exportAnimationPath{ "Animation.gif" }
        , m_pickerInteractorStyle{ vtkSmartPointer<PostMarkerInteractionStyle>::New() }//, m_markerDataTable(new FramelessDragableTableWidget(parentWidget()))
        , m_isActiveClip{ false }
        , m_planeNormalDirection{ 1., 0., 0. }
        , m_planeOrigin{ 0.,0.,0. }
        , m_clipperPlaneActor{ vtkSmartPointer<vtkActor>::New() }
        , m_isAddingPointMarker(false)
        , m_isAddingCellMarker(false)
        , m_isShowingGeometry(false)
        , m_animationInfoTextActor(nullptr)
        , m_isFirstTimeDataAnimation(true)
        , m_isBeginExportAnimation(false)
        , m_animationTooBarInfoData(nullptr)
        , m_animationCameraAngle(0.)
        , m_animationCameraCount(0)
        , m_animationDataCount(0)
        , m_scalarBarGeneratorMap()
        , m_cubeAxesActor(nullptr)
        //, m_curveMap()
        , m_isCubeAxesActorVisible(false)
    {
        vtkOutputWindow::SetGlobalWarningDisplay(0);

        m_qvtkWidget->setRenderWindow(m_renderWindow3D);

        m_renderWindow3D->AddRenderer(m_render3D);
        m_render3D->GetActiveCamera()->SetParallelProjection(true);
        m_render3D->AutomaticLightCreationOff();
        m_render3D->SetUseDepthPeeling(1);
        m_render3D->SetBackground(0.95, 0.95, 0.95);
        m_camera->ParallelProjectionOn();
        // m_render3D->SetGradientBackground(true);
        vtkNew<vtkLightKit> lightKit;
        lightKit->AddLightsToRenderer(m_render3D);
        lightKit->SetKeyLightWarmth(0.5);
        lightKit->SetFillLightWarmth(0.5);
        lightKit->SetBackLightWarmth(0.5);
        lightKit->SetHeadLightWarmth(0.5);
        m_dataTimer->setInterval(1000. / 3);
        m_cameraTimer->setInterval(1000. / 3);
        AddCornerAxes();
        initConnections();


        m_qvtkWidget->setContextMenuPolicy(Qt::CustomContextMenu);
        QObject::connect(m_qvtkWidget, &QVTKOpenGLNativeWidget::customContextMenuRequested,
            this, &Graph3DWindowBase::slot_addCustomContextMenu);
        m_windowToImageFilter->SetInput(m_renderWindow3D);
        m_pickerInteractorStyle->setRender(m_render3D);
        m_pickerInteractorStyle->setParentWindow(this);
        m_renderWindow3D->GetInteractor()->SetInteractorStyle(m_pickerInteractorStyle);

        m_markerDataTable = new FramelessDragableTableWidget(m_qvtkWidget);
        m_markerDataTable->getInnerTableWidget()->setEditTriggers(QAbstractItemView::NoEditTriggers);
        //m_markerDataTable->getInnerTableWidget()->setColumnCount(4);
        //QStringList nameList{ "标记名", "标记类型", "坐标", "值" };
        //m_markerDataTable->getInnerTableWidget()->setHorizontalHeaderLabels(nameList);
        //m_markerDataTable->setGeometry(20, 20, 300, 120);
        //m_markerDataTable->hide();
        //动图展示窗口
        m_animationTooBarInfoData = new AnimationTooBarInfoData(this);
        m_clipperPlaneActor->SetPickable(false);
        //vtkNew<vtkAxesActor> axesActor;
        //axesActor->SetTotalLength(0.1, 0.1, 0.1);
        //m_render3D->AddActor(axesActor);

    }

    Graph3DWindowBase::~Graph3DWindowBase()
    {
    }

    void Graph3DWindowBase::updatePickedDataDisplayWindowVisibility()
    {
        bool hasVisibleRow = false;
        int totalRows = m_markerDataTable->getInnerTableWidget()->rowCount();

        // 遍历所有行检查是否至少有一行是显示的
        for (int row = 0; row < totalRows; ++row)
        {
            if (!m_markerDataTable->getInnerTableWidget()->isRowHidden(row))
            {
                hasVisibleRow = true;
                break;
            }
        }
        hasVisibleRow ? m_markerDataTable->show() : m_markerDataTable->hide();
    }

    void Graph3DWindowBase::addAMarkerDataToPickedDataDisplayWindow(Marker3DDataInfo* info)
    {
        //int row = m_markerDataTable->getInnerTableWidget()->rowCount();
        //m_markerDataTable->getInnerTableWidget()->insertRow(row);
        //m_markerDataTable->getInnerTableWidget()->setItem(row, 0, new QTableWidgetItem(info.m_markerName));
        //QString markerType = info.m_markerType == 0 ? "Point" : "Cell";
        //m_markerDataTable->getInnerTableWidget()->setItem(row, 1, new QTableWidgetItem(markerType));
        //QString markerPosition = "(" + QString::number(info.m_markerPosition[0]) + "," + QString::number(info.m_markerPosition[1]) + "," + QString::number(info.m_markerPosition[2]) + ")";
        //m_markerDataTable->getInnerTableWidget()->setItem(row, 2, new QTableWidgetItem(markerPosition));
        //m_markerDataTable->getInnerTableWidget()->setItem(row, 3, new QTableWidgetItem(info.m_markerValue));
        //updatePickedDataDisplayWindowVisibility();
    }

    QHash<int, QString> Graph3DWindowBase::getMarkerNameMap()
    {
        QHash<int, QString> map;
        auto table = m_markerDataTable->getInnerTableWidget();
        for (int i = 0; i < table->rowCount(); ++i)
        {
            QString markerName = table->item(i, 1)->text();
            map.insert(i, markerName);
        }
        return map;
    }

    void Graph3DWindowBase::removeAMarkerDataFromPickedDataDisplayWindow(int markerId)
    {
        int rowCount = m_markerDataTable->getInnerTableWidget()->rowCount();
        for (int i = 0; i < rowCount; i++)
        {
            if (m_markerDataTable->getInnerTableWidget()->item(i, 0)->text().toInt() == markerId)
            {
                m_markerDataTable->getInnerTableWidget()->removeRow(i);
                break;
            }
        }
        updatePickedDataDisplayWindowVisibility();
    }

    void Graph3DWindowBase::clearPickedDataDisplayWindow()
    {
        m_markerDataTable->getInnerTableWidget()->clear();
        updatePickedDataDisplayWindowVisibility();
    }

    void Graph3DWindowBase::pyRemoveAllMarker()
    {
        this->slot_stopAddMarker();
        m_markerDataTable->getInnerTableWidget()->setRowCount(0);
        m_markerDataTable->hide();
        for (const auto& i : m_marker3DDataInfoList)
        {
            m_render3D->RemoveActor(i->m_markerActor);
            m_render3D->RemoveActor(i->m_markerTextActor);
        }
        m_marker3DDataInfoList.clear();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::pyExportTableData(const QString& fileName)
    {
        auto table = m_markerDataTable->getInnerTableWidget();
        CommonFunction::exportTableWidgetToCSV(table, fileName);
    }

    // void Graph3DWindowBase::addContextMenu(QWidget* parent, QVTKOpenGLNativeWidget* vtkWidget, Graph3DWindowBase* graph3DWindowBase)
    //{
    //     vtkWidget->setContextMenuPolicy(Qt::CustomContextMenu);

    //    // 连接右键菜单的信号
    //    QObject::connect(vtkWidget, &QVTKOpenGLNativeWidget::customContextMenuRequested, parent, [parent, vtkWidget](const QPoint& pos)
    //                     {
    //    QMenu contextMenu(parent);

    //    // 添加主菜单项
    //    QMenu* addMarkerMenu = contextMenu.addMenu("添加标记点");
    //    QAction* stopAddMarkerMenu = contextMenu.addAction("停止添加标记点");
    //    QAction* addPointMarker = addMarkerMenu->addAction("添加点标记");
    //    QAction* addFaceMarker = addMarkerMenu->addAction("添加面标记");
    //    QAction* deleteMarker = contextMenu.addAction("删除标记点");
    //    QAction* deleteAllMarkers = contextMenu.addAction("删除所有标记点");

    //    // 示例：禁用 "删除标记点" 菜单项
    //    deleteMarker->setEnabled(false);

    //    // 处理选项点击
    //    QObject::connect(addPointMarker, &QAction::triggered, this, &Graph3DWindowBase::slot_beginAddPointMarker);
    //    //                 {
    //    //    qDebug("添加点标记");
    //    //    graph3DWindowBase->beginAddPointMarker();
    //    //});

    //    //QObject::connect(addFaceMarker, &QAction::triggered, parent, [&](Graph3DWindowBase* object)
    //    //                 {
    //    //    qDebug("添加面标记");
    //    //    object->beginAddCellMarker();
    //    //});

    //    //QObject::connect(stopAddMarkerMenu, &QAction::triggered, parent, [&](Graph3DWindowBase* object)
    //    //                 {
    //    //    qDebug("停止添加标记");
    //    //    object->stopAddMarker () ;
    //    //});

    //    //QObject::connect(deleteMarker, &QAction::triggered, parent, [&](Graph3DWindowBase* object)
    //    //                 {
    //    //    qDebug("删除标记点");
    //    //    object->removeChosenMarker();
    //    //});

    //    //QObject::connect(deleteAllMarkers, &QAction::triggered, parent, [&](Graph3DWindowBase* object)
    //    //                 {
    //    //    qDebug("删除所有标记点");
    //    //    object->removeAllMarker();
    //    //});

    //    // 显示右键菜单
    //    contextMenu.exec(vtkWidget->mapToGlobal(pos)); });
    //}

    void Graph3DWindowBase::slot_beginAddPointMarker()
    {
        m_isAddingPointMarker = true;
        m_isAddingCellMarker = false;
        m_pickerInteractorStyle->setPickerType(0);
        m_pickerInteractorStyle->setEnablePicking(true);
        updateDataActorPickable();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::slot_beginAddCellMarker()
    {
        m_isAddingPointMarker = false;
        m_isAddingCellMarker = true;
        m_pickerInteractorStyle->setPickerType(1);
        m_pickerInteractorStyle->setEnablePicking(true);
        updateDataActorPickable();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::slot_stopAddMarker()
    {
        m_isAddingPointMarker = false;
        m_isAddingCellMarker = false;
        m_pickerInteractorStyle->setEnablePicking(false);
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::slot_removeChosenMarker()
    {
        this->slot_stopAddMarker();
        auto selectedItems = m_markerDataTable->getInnerTableWidget()->selectedItems();
        if (selectedItems.isEmpty())
        {
            return;
        }
        QSet<int> rowIds;
        QList<int> markerIds;
        for (auto item : selectedItems)
        {
            rowIds.insert(item->row());
        }

        for (auto i : rowIds)
        {
            auto markerIndex = m_markerDataTable->getInnerTableWidget()->item(i, 0)->text().toInt();
            markerIds.append(markerIndex);
        }

        QString markerIdStr = GlobalHelperInstance->convertIntListToQString(markerIds);
        int windowUniversalID = this->getWindowUniversalID();
        QStringList _pyCodes{};
        _pyCodes += QString("postmarkerremovechosensetting1 = PostProcessing.PostMarkerRemoveChosenSetting()");
        _pyCodes += QString("postmarkerremovechosensetting1.setWindowUniversalId(%1)").arg((int)windowUniversalID);
        _pyCodes += QString("postmarkerremovechosensetting1.setRemoveChosenId(\"%1\")").arg(markerIdStr);
        _pyCodes += QString("postmarkerremovechosensetting1.savePostMarkerRemoveChosenSettingData()");

        emit IBaseSignalInstance->signal_execPYCode(_pyCodes, true, false);

        removeChosenMarkers(markerIds);
        updatePickedDataDisplayWindowVisibility();
    }

    void Graph3DWindowBase::slot_removeAllMarker()
    {
        int windowUniversalID = this->getWindowUniversalID();
        QStringList _pyCodes{};
        _pyCodes += QString("postmarkerremoveallsetting1 = PostProcessing.PostMarkerRemoveAllSetting()");
        _pyCodes += QString("postmarkerremoveallsetting1.setWindowUniversalId(%1)").arg((int)windowUniversalID);
        _pyCodes += QString("postmarkerremoveallsetting1.savePostMarkerRemoveAllSettingData()");

        emit IBaseSignalInstance->signal_execPYCode(_pyCodes, true, false);

        pyRemoveAllMarker();
    }

    void Graph3DWindowBase::slot_exportTableData()
    {
        this->slot_stopAddMarker();
        QString filter = QObject::tr("CSV Files (*.csv);;Text Files (*.txt)");
        // 打开文件保存对话框，若用户在对话框中选择了文件，则返回对应的文件名
        QString fileName = QFileDialog::getSaveFileName(nullptr,
            QObject::tr("保存文件"),
            "",
            filter);

        // 如果选择为空，则用户可能取消了操作
        if (fileName.isEmpty())
            return;

        int windowUniversalID = this->getWindowUniversalID();
        QStringList _pyCodes{};
        _pyCodes += QString("postmarkerexporttabledata1 = PostProcessing.PostMarkerExportTableData()");
        _pyCodes += QString("postmarkerexporttabledata1.setWindowUniversalId(%1)").arg((int)windowUniversalID);
        _pyCodes += QString("postmarkerexporttabledata1.setFileName(\"%1\")").arg(fileName);
        _pyCodes += QString("postmarkerexporttabledata1.savePostMarkerExportTableData()");

        emit IBaseSignalInstance->signal_execPYCode(_pyCodes, true, false);

        pyExportTableData(fileName);
    }

    void Graph3DWindowBase::removeChosenMarkers(const QList<int>& markerIds)
    {
        for (const auto& id : markerIds)
        {
            auto info = m_marker3DDataInfoList.value(id);
            m_render3D->RemoveActor(info->m_markerActor);
            m_render3D->RemoveActor(info->m_markerTextActor);
            removeAMarkerDataFromPickedDataDisplayWindow(id);
            m_marker3DDataInfoList.remove(id);
        }
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::addMarkerDataInfo(int infomation3DBasicalId, int type, int fieldIndex, const QString& fieldName,
        int pickedId, const QString& value, vtkActor* pikerActor)
    {
        if (WindowTotalDataInstance->getInfomationGraph3DBasicalByID(infomation3DBasicalId) == nullptr)
            return;
        vtkActor* pickedProp = WindowTotalDataInstance->getInfomationGraph3DBasicalByID(infomation3DBasicalId)->m_actor;

        vtkDataSet* dataSet = pickedProp->GetMapper()->GetInput();
        double x(0), y(0), z(0);

        vtkSmartPointer<vtkActor> markerActor = nullptr;
        vtkSmartPointer<vtkDistanceToCamera> distanceToCamera = nullptr;
        if (type == 0)//点拾取
        {
            vtkPointData* pointData = dataSet->GetPointData();
            double* point = dataSet->GetPoint(pickedId);
            x = point[0];
            y = point[1];
            z = point[2];


            auto [markerActor1, m_distanceToCamera1, shpere1, fixedGlyph1] = generatePointMarkerActor(point[0], point[1], point[2]);
            markerActor = markerActor1;
            distanceToCamera = m_distanceToCamera1;
        }
        else if (type == 1)//cell拾取
        {
            markerActor = generateCellMarkerActor(dataSet, pickedId, x, y, z);
        }

        QString markerName = "Marker" + QString::number(++m_markerNum);

        QHash<int, QString> map = getMarkerNameMap();
        auto existNames = map.values();
        for (int i = 0; i < INT_MAX; ++i)
        {
            if (!existNames.contains(markerName))
            {
                break;
            }
            markerName = "Marker" + QString::number(++m_markerNum);
        }
        auto captionActor = generateMarkerTextActor(x, y, z, markerName);

        Marker3DDataInfo* info = new Marker3DDataInfo(this);
        info->m_markerId = m_pickedDataCount;
        info->m_fieldIndex = fieldIndex;
        info->m_pickedId = pickedId;
        info->m_markerName = markerName;
        info->m_markerType = type;
        info->m_fieldName = fieldName;
        info->m_markerPosition[0] = x;
        info->m_markerPosition[1] = y;
        info->m_markerPosition[2] = z;
        info->m_markerValue = value;
        info->m_markerActor = markerActor;
        info->m_markerTextActor = captionActor;
        info->m_sourceActor = pickedProp;
        info->m_graphInfoID = getGraphInfoIDByVtkActor(pickedProp);
        info->m_distanceToCamera = distanceToCamera;
        m_marker3DDataInfoList.insert(info->m_markerId, info);
        addAMarkerDataToPickedDataDisplayWindow(info);
        ++m_pickedDataCount;
        m_render3D->AddActor(markerActor);
        //m_addedMakerActor.append(markerActor);
        m_render3D->AddActor(captionActor);
    }

    void Graph3DWindowBase::updateMarkerValue()
    {
    }

    void Graph3DWindowBase::exportSourceDataToLocal()
    {
    }

    void Graph3DWindowBase::pyExportSourceDataToLocal(QString destPath)
    {
    }

    void Graph3DWindowBase::setActiveClipper(bool active)
    {
        m_isActiveClip = active;
    }

    bool Graph3DWindowBase::getActiveClipper() const
    {
        return m_isActiveClip;
    }

    void Graph3DWindowBase::setClipNormalDirection(double x, double y, double z)
    {
        m_planeNormalDirection[0] = x;
        m_planeNormalDirection[1] = y;
        m_planeNormalDirection[2] = z;
    }

    double* Graph3DWindowBase::getClipNormalDirection()
    {
        return m_planeNormalDirection;
    }

    void Graph3DWindowBase::setClipOrigin(double x, double y, double z)
    {
        m_planeOrigin[0] = x;
        m_planeOrigin[1] = y;
        m_planeOrigin[2] = z;
    }

    double* Graph3DWindowBase::getClipOrigin()
    {
        return m_planeOrigin;
    }


    void Graph3DWindowBase::setClipInsideOut(bool insideOut)
    {
        m_planeInsideOut = insideOut;
    }

    bool Graph3DWindowBase::getClipInsideOut() const
    {
        return m_planeInsideOut;
    }

    void Graph3DWindowBase::updateClipperAndGeometry(bool activeClipper, bool activeShowGeomtry, bool& hasAnyGeometrySuccess)
    {
        m_isActiveClip = activeClipper;
        m_isShowingGeometry = activeShowGeomtry;
        updateGeometryActors(activeShowGeomtry, hasAnyGeometrySuccess);//必须先更新几何显示，再更新clipper(影响辅助裁剪平面)
        updateClipper(activeClipper);
        m_clipperPlaneActor->SetVisibility(activeClipper);
        m_render3D->ResetCamera();
        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::updateClipper(bool activeClipper)
    {

    }

    // 函数说明：
    // 输入:
    //   actors: QList<vtkSmartPointer<vtkActor>>，包含所有需要计算包围盒的actor
    //   planeNormal: 长度为3的数组，表示平面的法向。例如 {0,0,1} 表示 XY 平面
    //   origin: 长度为3的数组，表示平面所在的常数坐标（即 planeNormal 对应的坐标值用 origin 中的值）
    // 处理：计算所有 actor 的总体边界包围盒，根据包围盒对应面的范围扩展 20%，生成平面。
    bool Graph3DWindowBase::CreateExpandedPlaneActor(
        const QList<vtkActor*>& actors,
        const double planeNormal[3],
        const double origin[3])
    {
        // 1. 计算所有 actor 的总体包围盒
        vtkBoundingBox bbox;
        for (int i = 0; i < actors.count(); ++i)
        {
            double b[6];
            actors[i]->GetBounds(b);  // b: {xmin, xmax, ymin, ymax, zmin, zmax}
            bbox.AddBounds(b);
        }
        double b[6];
        bbox.GetBounds(b);
        // b[0]=xmin, b[1]=xmax, b[2]=ymin, b[3]=ymax, b[4]=zmin, b[5]=zmax

        // 2. 计算扩展量（每边增加10%，总尺寸扩大20%）
        const double expandX = (b[1] - b[0]) * 0.1;
        const double expandY = (b[3] - b[2]) * 0.1;
        const double expandZ = (b[5] - b[4]) * 0.1;

        // 3. 根据平面法向生成对应平面
        vtkSmartPointer<vtkPlaneSource> planeSource = vtkSmartPointer<vtkPlaneSource>::New();
        const double tol = 1e-6;
        if (std::fabs(std::fabs(planeNormal[0]) - 1.0) < tol && std::fabs(planeNormal[1]) < tol && std::fabs(planeNormal[2]) < tol)
        {
            // 法向平行于 X 轴，则平面与 YZ 平面平行，x 坐标固定为 origin[0]
            const double xConst = origin[0];
            double yMinExpanded = b[2] - expandY;
            double yMaxExpanded = b[3] + expandY;
            double zMinExpanded = b[4] - expandZ;
            double zMaxExpanded = b[5] + expandZ;

            // 选择矩形的三个角点
            // Origin：左下角；Point1：右下角（沿 Y 方向）；Point2：左上角（沿 Z 方向）
            planeSource->SetOrigin(xConst, yMinExpanded, zMinExpanded);
            planeSource->SetPoint1(xConst, yMaxExpanded, zMinExpanded);
            planeSource->SetPoint2(xConst, yMinExpanded, zMaxExpanded);
        }
        else if (std::fabs(std::fabs(planeNormal[1]) - 1.0) < tol && std::fabs(planeNormal[0]) < tol && std::fabs(planeNormal[2]) < tol)
        {
            // 法向平行于 Y 轴，则平面与 XZ 平面平行，y 坐标固定为 origin[1]
            const double yConst = origin[1];
            double xMinExpanded = b[0] - expandX;
            double xMaxExpanded = b[1] + expandX;
            double zMinExpanded = b[4] - expandZ;
            double zMaxExpanded = b[5] + expandZ;

            planeSource->SetOrigin(xMinExpanded, yConst, zMinExpanded);
            planeSource->SetPoint1(xMaxExpanded, yConst, zMinExpanded);
            planeSource->SetPoint2(xMinExpanded, yConst, zMaxExpanded);
        }
        else if (std::fabs(std::fabs(planeNormal[2]) - 1.0) < tol && std::fabs(planeNormal[0]) < tol && std::fabs(planeNormal[1]) < tol)
        {
            // 法向平行于 Z 轴，则平面与 XY 平面平行，z 坐标固定为 origin[2]
            const double zConst = origin[2];
            double xMinExpanded = b[0] - expandX;
            double xMaxExpanded = b[1] + expandX;
            double yMinExpanded = b[2] - expandY;
            double yMaxExpanded = b[3] + expandY;

            planeSource->SetOrigin(xMinExpanded, yMinExpanded, zConst);
            planeSource->SetPoint1(xMaxExpanded, yMinExpanded, zConst);
            planeSource->SetPoint2(xMinExpanded, yMaxExpanded, zConst);
        }
        else
        {
            return false;
        }

        planeSource->Update();

        // 4. 将平面数据包装成 mapper 和 actor 返回
        vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
        mapper->SetInputConnection(planeSource->GetOutputPort());

        m_clipperPlaneActor->SetMapper(mapper);
        m_clipperPlaneActor->GetProperty()->SetColor(0.6, 0.6, 0.6);
        m_clipperPlaneActor->GetProperty()->SetOpacity(0.3);
        return true;
    }

    bool Graph3DWindowBase::CreateExpandedPlaneActor(const QList<vtkDataSet*>& dataList,
        const double planeNormal[3], const double origin[3])
    {
        // 1. 计算所有 actor 的总体包围盒
        vtkBoundingBox bbox;
        for (int i = 0; i < dataList.count(); ++i)
        {
            if (dataList[i] == nullptr)
            {
                continue;
            }
            double b[6];
            dataList[i]->GetBounds(b);  // b: {xmin, xmax, ymin, ymax, zmin, zmax}
            bbox.AddBounds(b);
        }
        double b[6];
        bbox.GetBounds(b);
        // b[0]=xmin, b[1]=xmax, b[2]=ymin, b[3]=ymax, b[4]=zmin, b[5]=zmax

        // 2. 计算扩展量（每边增加10%，总尺寸扩大20%）
        const double expandX = (b[1] - b[0]) * 0.1;
        const double expandY = (b[3] - b[2]) * 0.1;
        const double expandZ = (b[5] - b[4]) * 0.1;

        // 3. 根据平面法向生成对应平面
        vtkSmartPointer<vtkPlaneSource> planeSource = vtkSmartPointer<vtkPlaneSource>::New();
        const double tol = 1e-6;
        if (std::fabs(std::fabs(planeNormal[0]) - 1.0) < tol && std::fabs(planeNormal[1]) < tol && std::fabs(planeNormal[2]) < tol)
        {
            // 法向平行于 X 轴，则平面与 YZ 平面平行，x 坐标固定为 origin[0]
            const double xConst = origin[0];
            double yMinExpanded = b[2] - expandY;
            double yMaxExpanded = b[3] + expandY;
            double zMinExpanded = b[4] - expandZ;
            double zMaxExpanded = b[5] + expandZ;

            // 选择矩形的三个角点
            // Origin：左下角；Point1：右下角（沿 Y 方向）；Point2：左上角（沿 Z 方向）
            planeSource->SetOrigin(xConst, yMinExpanded, zMinExpanded);
            planeSource->SetPoint1(xConst, yMaxExpanded, zMinExpanded);
            planeSource->SetPoint2(xConst, yMinExpanded, zMaxExpanded);
        }
        else if (std::fabs(std::fabs(planeNormal[1]) - 1.0) < tol && std::fabs(planeNormal[0]) < tol && std::fabs(planeNormal[2]) < tol)
        {
            // 法向平行于 Y 轴，则平面与 XZ 平面平行，y 坐标固定为 origin[1]
            const double yConst = origin[1];
            double xMinExpanded = b[0] - expandX;
            double xMaxExpanded = b[1] + expandX;
            double zMinExpanded = b[4] - expandZ;
            double zMaxExpanded = b[5] + expandZ;

            planeSource->SetOrigin(xMinExpanded, yConst, zMinExpanded);
            planeSource->SetPoint1(xMaxExpanded, yConst, zMinExpanded);
            planeSource->SetPoint2(xMinExpanded, yConst, zMaxExpanded);
        }
        else if (std::fabs(std::fabs(planeNormal[2]) - 1.0) < tol && std::fabs(planeNormal[0]) < tol && std::fabs(planeNormal[1]) < tol)
        {
            // 法向平行于 Z 轴，则平面与 XY 平面平行，z 坐标固定为 origin[2]
            const double zConst = origin[2];
            double xMinExpanded = b[0] - expandX;
            double xMaxExpanded = b[1] + expandX;
            double yMinExpanded = b[2] - expandY;
            double yMaxExpanded = b[3] + expandY;

            planeSource->SetOrigin(xMinExpanded, yMinExpanded, zConst);
            planeSource->SetPoint1(xMaxExpanded, yMinExpanded, zConst);
            planeSource->SetPoint2(xMinExpanded, yMaxExpanded, zConst);
        }
        else
        {
            return false;
        }

        planeSource->Update();

        // 4. 将平面数据包装成 mapper 和 actor 返回
        vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
        mapper->SetInputConnection(planeSource->GetOutputPort());

        m_clipperPlaneActor->SetMapper(mapper);
        m_clipperPlaneActor->GetProperty()->SetColor(0.6, 0.6, 0.6);
        m_clipperPlaneActor->GetProperty()->SetOpacity(0.3);
        return true;
    }



    void Graph3DWindowBase::slot_scalarBarDoubleClicked(vtkProp* prop)
    {
        qDebug() << "Graph3DWindowBase::slot_scalarBarDoubleClicked";
        // m_mainSettingDialog->show();
    }

    void Graph3DWindowBase::exportAnimationData(const QString& fileName)
    {
        qDebug() << __func__;
    }

    void Graph3DWindowBase::updateControlPanel()
    {
    }

    void Graph3DWindowBase::initAnimationInfoTextActor()
    {
        if (m_animationInfoTextActor != nullptr)
        {
            return;
        }
        m_animationInfoTextActor = vtkSmartPointer<vtkTextActor>::New();
        // 多行文本使用 "\n" 换行符
        m_animationInfoTextActor->SetInput("");
        m_animationInfoTextActor->GetTextProperty()->SetFontSize(14);
        m_animationInfoTextActor->GetTextProperty()->SetColor(0.0, 0.0, 0.0);
        m_animationInfoTextActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
        m_animationInfoTextActor->SetPosition(0.8, 0.9);
        m_render3D->AddActor2D(m_animationInfoTextActor);

        QString currenEXEPath = QCoreApplication::applicationDirPath();
        auto micFont = currenEXEPath + "/Fonts/Alibaba-PuHuiTi-Regular.ttf";
        m_animationInfoTextActor->GetTextProperty()->SetFontFamily(VTK_FONT_FILE);
        m_animationInfoTextActor->GetTextProperty()->SetFontFile(micFont.toLocal8Bit());
    }

    vtkSmartPointer<vtkTextActor> Graph3DWindowBase::getAnimationInfoTextActor()
    {
        return m_animationInfoTextActor;
    }

    void Graph3DWindowBase::setAnimationInfoText(const QStringList& text)
    {
        if (m_animationInfoTextActor == nullptr)
        {
            return;
        }

        QString _text = text.join("\n");
        m_animationInfoTextActor->SetInput(_text.toUtf8().constData());
    }

    QStringList  Graph3DWindowBase::getAnimationInfoText() const
    {
        if (m_animationInfoTextActor == nullptr)
        {
            return QStringList();
        }

        const char* rawText = m_animationInfoTextActor->GetInput();
        if (!rawText)
        {
            return QStringList();
        }
        QString text = QString::fromUtf8(rawText);

        return text.split("\n");
    }

    bool Graph3DWindowBase::isInfoContainsMultipleData(const InfomationGraph3DBasical* info)
    {
        return false;
    }

    bool Graph3DWindowBase::isInfoFromSameSource(QList<InfomationGraph3DBasical*> infoList)
    {
        return false;
    }

    bool Graph3DWindowBase::whetherCanEnableDataAnimation()
    {
        return false;
    }

    void Graph3DWindowBase::updateAnimationButtonState()
    {
        emit IWidgetSignalInstance->signal_updateAnimationButtonState();
    }

    void Graph3DWindowBase::setFrameToBeforeExportAnimation()
    {
        for (auto info : m_graphInfoList)
        {
            auto fieldGraphGenerator = info->m_fieldGraphGenerator;
            fieldGraphGenerator->SetInputDataSet(info->m_animationDataList.at(info->m_lastFrameIndexBeforeExportAnimation));
            info->m_currentAnimationIndex = info->m_lastFrameIndexBeforeExportAnimation;
            if (info->m_valueGroupNameIndex != -1)
            {
                const auto& currentField = info->m_currentScalarArray;
                auto isSetSuccess = fieldGraphGenerator->SetActiveScalarAttribute(
                    currentField.arrayIndex, currentField.type, 0);
                if (!isSetSuccess)
                {
                    return;
                }
                fieldGraphGenerator->UpdateScalarFieldActor();
            }
            else
            {
                fieldGraphGenerator->UpdateGeometryActor();
            }
            sentToolBarAnimationDataInfo(info);
        }
        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::sentToolBarAnimationDataInfo(const InfomationGraph3DBasical* info)
    {
    }

    void Graph3DWindowBase::setAnimationTooBarInfoData(AnimationTooBarInfoData* info)
    {
        m_animationTooBarInfoData = info;
    }

    AnimationTooBarInfoData* Graph3DWindowBase::getAnimationTooBarInfoData()
    {
        return m_animationTooBarInfoData;
    }

    void Graph3DWindowBase::AddCornerAxes()
    {
        m_leftDownCornerAxes->SetPosition(0, 0, 0);
        m_leftDownCornerAxes->SetTotalLength(12, 12, 12);
        m_leftDownCornerAxes->SetShaftType(0);
        m_leftDownCornerAxes->SetCylinderRadius(0.02);

        m_leftDownCornerWidget->SetOrientationMarker(m_leftDownCornerAxes);
        m_leftDownCornerWidget->SetInteractor(m_renderWindow3D->GetInteractor());
        m_leftDownCornerWidget->KeyPressActivationOff();
        m_leftDownCornerWidget->SetEnabled(1);
        m_leftDownCornerWidget->InteractiveOff();

        // m_render3D->ResetCamera();
        // qvtkWidget->renderWindow()->Render();
    }

    void Graph3DWindowBase::initConnections()
    {
        connect(m_dataTimer, &QTimer::timeout, this, &Graph3DWindowBase::slot_beginToPlayDataAnimation);
        connect(m_cameraTimer, &QTimer::timeout, this, &Graph3DWindowBase::slot_beginToPlayCameraAnimation);
        connect(IWidgetSignalInstance, &BaseWidget::IWidgetSignal::signal_exitAddMarkerState, this, &Graph3DWindowBase::slot_exitAddMarkerState);
    }

    void Graph3DWindowBase::ResetCameraViewDirection(vtkCamera* camera,
        double look_x, double look_y, double look_z, double up_x, double up_y, double up_z)
    {
        double pos[3] = { 0, 0, 0 };
        double focal_point[3] = { look_x, look_y, look_z };
        double view_up[3] = { up_x, up_y, up_z };
        camera->SetPosition(pos);
        camera->SetFocalPoint(focal_point);
        camera->SetViewUp(view_up);

        m_render3D->ResetCamera();
        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::exportAnimationToLocal()
    {
        bool _isOK = false;
        if (m_animationImageList.empty())
        {
            emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图失败！"), false);
            auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图失败！"), EDialogMsg::Error, QStringLiteral("失败"), { QStringLiteral("确定") });
            MainWindowSignalHandler::getInstance()->setToolButtonExportMotionGraphState(true);
            emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
            return;
        }
        qDebug() << "m_animationImageList.size(): " << m_animationImageList.size();
        int frameWidth = m_animationImageList[0].cols;
        int frameHeight = m_animationImageList[0].rows;
        cv::VideoWriter videoWriter;
        QFileInfo fileInfo(m_exportAnimationPath);
        auto suffix = fileInfo.suffix();
        auto frameRate = m_animationSettingData->frameRate();

        if (suffix == "gif")
        {
            _isOK = CommonFunction::saveGifFromMatList(m_animationImageList, m_exportAnimationPath, frameRate);
            //gif的成功失败对话框在函数里面完成
        }
        else if (suffix == "avi")
        {
            //auto animationTempIODir = CommonFunction::createAnimationTempIODir("animationTempIO");
            //QString tempFileName = animationTempIODir + "/tempVideo.avi";


            videoWriter.open(m_exportAnimationPath.toUtf8().constData(), cv::VideoWriter::fourcc('X', 'V', 'I', 'D'),
                frameRate, cv::Size(frameWidth, frameHeight), true);

            if (!videoWriter.isOpened())
            {
                qDebug() << "Could not open the output video file.";
                emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图失败！"), false);
                auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图失败！"), EDialogMsg::Error, QStringLiteral("失败"), { QStringLiteral("确定") });
                MainWindowSignalHandler::getInstance()->setToolButtonExportMotionGraphState(true);
                emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
                return;
            }

            for (const auto& image : m_animationImageList)
            {
                videoWriter.write(image);
            }

            videoWriter.release();
            _isOK = true;

            //_isOK = CommonFunction::copyAndRenameFile(tempFileName, m_exportAnimationPath);

            //CommonFunction::deleteAnimationTempIODir(animationTempIODir);
            if (_isOK)
            {
                emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图完成！"), false);
                auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图成功！"), EDialogMsg::Normal, QStringLiteral("成功"), { QStringLiteral("确定") });
                MainWindowSignalHandler::getInstance()->setToolButtonExportMotionGraphState(true);
            }
            else
            {
                emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图失败！"), false);
                auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图失败！"), EDialogMsg::Error, QStringLiteral("失败"), { QStringLiteral("确定") });
                MainWindowSignalHandler::getInstance()->setToolButtonExportMotionGraphState(true);
            }
        }
        emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
    }

    void Graph3DWindowBase::animationToNextDataFrame()
    {
    }

    std::tuple<vtkSmartPointer<vtkActor>, vtkSmartPointer<vtkDistanceToCamera>,
        vtkSmartPointer<vtkSphereSource>, vtkSmartPointer<vtkGlyph3D>>
        Graph3DWindowBase::generatePointMarkerActor(double x, double y, double z)
    {
        vtkNew<vtkPointSource> fixedPointSource;
        fixedPointSource->SetCenter(x, y, z);
        fixedPointSource->SetRadius(0.000001);
        fixedPointSource->SetNumberOfPoints(1);

        vtkSmartPointer<vtkDistanceToCamera> m_distanceToCamera = vtkSmartPointer<vtkDistanceToCamera>::New();
        m_distanceToCamera->SetInputConnection(fixedPointSource->GetOutputPort());
        m_distanceToCamera->SetScreenSize(30);

        vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();
        vtkSmartPointer< vtkGlyph3D> fixedGlyph = vtkSmartPointer<vtkGlyph3D>::New();
        fixedGlyph->SetInputConnection(m_distanceToCamera->GetOutputPort());
        fixedGlyph->SetSourceConnection(sphere->GetOutputPort());
        fixedGlyph->SetScaleModeToScaleByScalar();
        fixedGlyph->SetInputArrayToProcess(
            0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");

        vtkSmartPointer<vtkPolyDataMapper> m_moveMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
        m_moveMapper->SetInputConnection(fixedGlyph->GetOutputPort());
        m_moveMapper->SetScalarVisibility(false);

        vtkSmartPointer< vtkActor> m_markerActor = vtkSmartPointer<vtkActor>::New();
        m_markerActor->SetMapper(m_moveMapper);
        vtkNew<vtkNamedColors> colors;
        m_markerActor->GetProperty()->SetColor(colors->GetColor3d("red").GetData());
        m_markerActor->SetPickable(false);
        //m_markerActor->SetVisibility(false);
        m_distanceToCamera->SetRenderer(m_render3D);

        return std::make_tuple(m_markerActor, m_distanceToCamera, sphere, fixedGlyph);
    }

    vtkSmartPointer<vtkActor> Graph3DWindowBase::generateCellMarkerActor(
        vtkDataSet* dataSet, int pickedId, double& x, double& y, double& z)
    {
        vtkNew<vtkIdList> idList;
        vtkNew<vtkExtractCells> extractCells;
        vtkNew<vtkGeometryFilter> geometryFilter;
        vtkNew<vtkFeatureEdges> featureEdges;
        vtkNew<vtkDataSetMapper> edgeMapper;
        vtkNew<vtkActor> edgeActor;

        extractCells->SetInputData(dataSet);
        idList->Reset();
        idList->InsertNextId(pickedId);
        extractCells->SetCellList(idList);
        extractCells->Update();
        geometryFilter->SetInputConnection(extractCells->GetOutputPort());
        featureEdges->SetInputConnection(geometryFilter->GetOutputPort());
        featureEdges->BoundaryEdgesOn(); // 只保留边界边
        featureEdges->FeatureEdgesOn();
        featureEdges->NonManifoldEdgesOn();
        featureEdges->ManifoldEdgesOn();
        featureEdges->Update();
        edgeMapper->SetInputConnection(featureEdges->GetOutputPort());
        edgeActor->SetMapper(edgeMapper);
        edgeActor->GetProperty()->SetColor(0.0, 1.0, 0.0);
        edgeActor->GetProperty()->SetLineWidth(3.0);
        edgeActor->SetPickable(false);

        // 获取中心
        vtkCell* cell = dataSet->GetCell(pickedId);
        int npts = cell->GetNumberOfPoints();
        double center[3] = { 0.0, 0.0, 0.0 };

        // 遍历 cell 的所有点，累加其坐标
        for (int i = 0; i < npts; ++i)
        {
            double pt[3];
            dataSet->GetPoint(cell->GetPointId(i), pt);
            center[0] += pt[0];
            center[1] += pt[1];
            center[2] += pt[2];
        }
        // 求平均值得到质心
        center[0] /= npts;
        center[1] /= npts;
        center[2] /= npts;

        x = center[0];
        y = center[1];
        z = center[2];

        return edgeActor;
    }

    vtkSmartPointer<vtkCaptionActor2D> Graph3DWindowBase::generateMarkerTextActor(
        double x, double y, double z, const QString& text)
    {
        vtkNew<vtkCaptionActor2D> caption;
        caption->SetCaption(text.toStdString().c_str());
        caption->SetAttachmentPoint(x, y, z);
        caption->SetLeader(1);
        caption->SetLeaderGlyphSize(1);
        caption->BorderOff();
        caption->GetTextActor()->GetTextProperty()->SetFontSize(10);
        caption->GetTextActor()->GetTextProperty()->SetColor(1.0, 0.0, 0.0);
        caption->GetTextActor()->SetTextScaleModeToNone();
        caption->GetTextActor()->GetTextProperty()->SetJustificationToCentered();
        caption->GetTextActor()->GetTextProperty()->SetVerticalJustificationToCentered();
        return caption;
    }

    void Graph3DWindowBase::slot_beginToPlayDataAnimation()
    {
        animationToNextDataFrame();

        // 每次都从m_animationSettingData更新数据
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        if (m_isExportingAnimation) // 导出动画状态
        {
            // 先判断是否应该结束,再截图
            if (m_loopCount * m_animationSettingData->sampleRate() >= m_imageSize)
            {
                setActiveAnimation(false);
                m_isExportingAnimation = false;
                setFrameToBeforeExportAnimation();
                exportAnimationToLocal();
                return;
            }
            ++m_loopCount;

            vtkNew<vtkWindowToImageFilter> windowToImageFilter;
            windowToImageFilter->SetInput(m_renderWindow3D);
            windowToImageFilter->Update();
            auto vtkImage = windowToImageFilter->GetOutput();
            cv::Mat image = CommonFunction::VTKImageDataToCVMat(vtkImage);
            m_animationImageList.append(image);
        }
    }

    void Graph3DWindowBase::slot_beginToPlayCameraAnimation()
    {
        auto thetaAngle = m_animationSettingData->thetaAngle();
        auto phiAngle = m_animationSettingData->phiAngle();
        auto isThetaChecked = m_animationSettingData->isThetaChecked();
        m_cameraTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        double currentAngle = 0.0;
        int currentLoopCount = 0;
        double angle = 0;
        if (m_isExportingAnimation) // 导出动画状态
        {
            vtkNew<vtkWindowToImageFilter> windowToImageFilter;
            windowToImageFilter->SetInput(m_renderWindow3D);
            windowToImageFilter->Update();
            auto vtkImage = windowToImageFilter->GetOutput();
            cv::Mat image = CommonFunction::VTKImageDataToCVMat(vtkImage);
            m_animationImageList.append(image);
            // 先截图，再判断是否应该结束
            if (thetaAngle)
            {
                angle = thetaAngle;
            }
            else
            {
                angle = phiAngle;
            }

            if (m_loopCount * angle >= 360.)
            {
                setActiveAnimation(false);
                // 停止截图并保存
                m_isExportingAnimation = false;
                // m_dataTimer->stop();
                exportAnimationToLocal();
                return;
            }
            m_loopCount++;
        }
        int totalNum = 360;
        auto activeCamera = m_render3D->GetActiveCamera();
        if (isThetaChecked)
        {
            activeCamera->Elevation(thetaAngle);
            activeCamera->OrthogonalizeViewUp();
            totalNum = std::ceil(360 / thetaAngle);
            angle = thetaAngle;
        }
        else
        {
            activeCamera->Azimuth(phiAngle);
            totalNum = std::ceil(360 / phiAngle);
            angle = phiAngle;
        }

        m_animationCameraAngle += angle;

        ++m_animationCameraCount;
        if (m_animationCameraAngle >= 360)
        {
            m_animationCameraCount = 0;
        }
        m_animationCameraAngle = CommonFunction::normalizeAngle(m_animationCameraAngle);

        double currentValue = m_animationCameraAngle;
        QString firstLine = "角度：" + QString::number(currentValue) + " " + "deg";

        auto frameRate = m_animationSettingData->frameRate();
        QString thirdLine = "播放速度：" + QString::number(frameRate);

        QStringList textActorList;
        textActorList << firstLine << thirdLine;
        setAnimationInfoText(textActorList);

        if (m_animationTooBarInfoData != nullptr)
        {
            m_animationTooBarInfoData->setType("角度");
            m_animationTooBarInfoData->setUnit("deg");
            m_animationTooBarInfoData->setCurrentValue(QString::number(currentValue));
            m_animationTooBarInfoData->setSliderCurrentValue(m_animationCameraCount);
            m_animationTooBarInfoData->setSliderMaxValue(totalNum);
        }
        emit IWidgetSignalInstance->signal_setToolBarAnimationDataInfo("角度", "deg", currentValue, totalNum, m_animationCameraCount);

        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::addGraph3D(InfomationGraph3DBasical* info)
    {
        qDebug() << "Graph3DWindowBase::addGraph3D";
    }

    bool Graph3DWindowBase::saveScreenShot(const QString& path)
    {
        CommonFunction::saveJPEGImageFromVTKScreen(m_renderWindow3D, 1920, 1080, path, true);
        return true;
    }

    void Graph3DWindowBase::setResetCamera()
    {
        m_render3D->ResetCamera();
        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::setCameraToPositiveX()
    {
        ResetCameraViewDirection(m_camera, 1, 0, 0, 0, 0, 1);
    }

    void Graph3DWindowBase::setCameraToPositiveY()
    {
        ResetCameraViewDirection(m_camera, 0, 1, 0, 0, 0, 1);
    }

    void Graph3DWindowBase::setCameraToPositiveZ()
    {
        ResetCameraViewDirection(m_camera, 0, 0, 1, 0, 1, 0);
    }

    void Graph3DWindowBase::setCameraToNegativeX()
    {
        ResetCameraViewDirection(m_camera, -1, 0, 0, 0, 0, 1);
    }

    void Graph3DWindowBase::setCameraToNegativeY()
    {
        ResetCameraViewDirection(m_camera, 0, -1, 0, 0, 0, 1);
    }

    void Graph3DWindowBase::setCameraToNegativeZ()
    {
        ResetCameraViewDirection(m_camera, 0, 0, -1, 0, 1, 0);
    }

    void Graph3DWindowBase::setCameraToIsometric()
    {
        ResetCameraViewDirection(m_camera, -1, -1, -1, 0, 0, 1);
    }

    void Graph3DWindowBase::zoomCamera(double scale)
    {
        vtkCamera* camera = m_render3D->GetActiveCamera();
        camera->Zoom(scale);
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::setInteractorStlyleToTranslation()
    {
        m_pickerInteractorStyle->setInnerInteractionStyle(1);
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::setInteractorStlyleToRotation()
    {
        m_pickerInteractorStyle->setInnerInteractionStyle(0);
        m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::setInteractorStlyleToDefault()
    {
        setInteractorStlyleToRotation();
    }

    void Graph3DWindowBase::setInteractorStlyleToPicking()
    {
        //m_renderWindow3D->GetInteractor()->SetInteractorStyle(m_pickerInteractorStyle);
        //m_renderWindow3D->Render();
    }

    void Graph3DWindowBase::updateGeometryActors(bool isShow, bool& hasAnyGeometrySuccess)
    {
        bool _hasAnyGeometry = false;
        m_isShowingGeometry = isShow;
        QList<QStringList> m_updatedList;

        for (auto info : m_graphInfoList)
        {
            const auto& path = info->m_geometryFileName;
            if(path.empty())
            {
                continue;
            }

            //auto isAdded = info->m_isGeomtryActorImported;
            //auto geometryActor = info->m_geometryActor;
            if (isShow)
            {
                if (m_updatedList.contains(path))
                {
                    continue;
                }

                //if (info->m_geomtryActorGenerator == nullptr)
                //{
                //    GeomtryActorGenerator* generator = new GeomtryActorGenerator(this);
                //    generator->setFileNameList(path);
                //    //generator->setIsActiveClip(m_isActiveClip);
                //    //generator->setClipPlaneNormalDirection(m_planeNormalDirection);
                //    //generator->setClipPlaneOrigin(m_planeOrigin);
                //    //generator->updateActor();
                //    info->m_geomtryActorGenerator = generator;
                //    //info->m_geometryActor = generator->getActor();
                //    m_render3D->AddActor(generator->getActor());
                //}

                m_updatedList.append(path);

                info->m_geomtryActorGenerator->setIsActiveClip(m_isActiveClip);
                info->m_geomtryActorGenerator->setClipPlaneNormalDirection(m_planeNormalDirection);
                info->m_geomtryActorGenerator->setClipPlaneOrigin(m_planeOrigin);
                int status = info->m_geomtryActorGenerator->updateActor();
                if (status == 2)//全部失败的情况
                {
                    //QString _errorText = info->m_geomtryActorGenerator->getErrorMessage();
                    //emit IBaseSignalInstance->signal_sendErrorMessageToUI(_errorText);
                    //delete info->m_geomtryActorGenerator;
                    //info->m_geomtryActorGenerator = nullptr;
                    continue;
                }
                //else if (status == 1)//部分失败的情况，继续执行
                //{
                //    QString _errorText = info->m_geomtryActorGenerator->getErrorMessage();
                //    emit IBaseSignalInstance->signal_sendErrorMessageToUI(_errorText);
                //}

                info->m_geomtryActorGenerator->getActor()->SetVisibility(1);
                m_render3D->AddActor(info->m_geomtryActorGenerator->getActor());
                _hasAnyGeometry = true;
            }
            else
            {
                if (info->m_geomtryActorGenerator != nullptr)
                {
                    info->m_geomtryActorGenerator->getActor()->SetVisibility(0);
                    _hasAnyGeometry = true;
                }
            }
        }

        if (isShow && !_hasAnyGeometry)//全部失败的情况
        {
            emit IBaseSignalInstance->signal_sendErrorMessageToUI("未读取到有效几何文件");
            m_isShowingGeometry = false;//没有几何文件导入成功的情况下，要将显示状态设为false,同时显示模型的按钮设置为未选中
        }
        hasAnyGeometrySuccess = _hasAnyGeometry;

        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    vtkSmartPointer<vtkActor> Graph3DWindowBase::importGeomtryActor(const QString& path, bool resetCamera)
    {
        STEPControl_Reader aReader;
        TopoDS_Shape aShape;

        if (aReader.ReadFile((Standard_CString)path.toUtf8()) != IFSelect_RetDone)
        {
            qDebug() << "Cannot open STEP file";
            return nullptr;
        }
        aReader.TransferRoots();
        aShape = aReader.OneShape();
        // 必须有编号 否则拾取时无法被区分 m_picker->GetPickedActors();会拾取到所有的actor
        // static Standard_Integer ShapeID = 0; ++ShapeID;
        Handle(IVtkOCC_Shape) aShapeWrapper = new IVtkOCC_Shape(aShape);
        // aShapeWrapper->SetId(ShapeID);

        /// 高集成方案
        vtkNew<IVtkTools_ShapeDataSource> occSource;
        occSource->SetShape(aShapeWrapper);

        ////        //celan
        // vtkNew<vtkCleanPolyData> cleaner;
        // cleaner->SetInputConnection(occSource->GetOutputPort());
        // cleaner->Update();

        vtkNew<IVtkTools_DisplayModeFilter> DMfilter; // 显示模式
        DMfilter->AddInputConnection(occSource->GetOutputPort());
        DMfilter->SetDisplayMode(DM_Shading);
        DMfilter->SetFaceBoundaryDraw(true);
        DMfilter->SetSmoothShading(true);

        vtkNew<vtkPolyDataNormals> DataNormalsFileter;
        DataNormalsFileter->SetInputConnection(DMfilter->GetOutputPort());
        DataNormalsFileter->FlipNormalsOn();
        DataNormalsFileter->Update();

        vtkNew<vtkPolyDataMapper> mapper;
        mapper->SetInputConnection(DataNormalsFileter->GetOutputPort());
        vtkSmartPointer<vtkActor> geomtryActor = vtkSmartPointer<vtkActor>::New();
        geomtryActor->SetMapper(mapper);
        m_render3D->AddActor(geomtryActor);
        if (resetCamera)
        {
            m_render3D->ResetCamera();
        }
        m_renderWindow3D->Render();

        return geomtryActor;
    }

    void Graph3DWindowBase::setAnimationType(int type)
    {
        m_animationType = type;
    }

    int Graph3DWindowBase::getAnimationType() const
    {
        return m_animationType;
    }

    void Graph3DWindowBase::increaseAnimationSpeed()
    {
        if (m_animationSpeed < 2.0)
        {
            m_animationSpeed += 0.1;
        }
        m_animationSpeed = qMin(2.0, m_animationSpeed);
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        qDebug() << m_dataTimer->interval();
    }

    void Graph3DWindowBase::decreaseAnimationSpeed()
    {
        if (m_animationSpeed > 0.1)
        {
            m_animationSpeed -= 0.1;
        }
        m_animationSpeed = qMax(0.1, m_animationSpeed);
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        qDebug() << m_dataTimer->interval();
    }

    double Graph3DWindowBase::getSpeedFactor() const
    {
        return m_animationSpeed;
    }

    void Graph3DWindowBase::setAnimationSettingData(AnimationSettingData* data)
    {
        m_animationSettingData = data;
    }

    AnimationSettingData* Graph3DWindowBase::getAnimationSettingData() const
    {
        return m_animationSettingData;
    }

    void Graph3DWindowBase::setActiveAnimation(bool active)
    {
        qDebug() << __func__;
    }

    void Graph3DWindowBase::slot_addCustomContextMenu(const QPoint& pos)
    {
        QMenu contextMenu(this);

        // 添加主菜单项
        if (!m_isAddingCellMarker && !m_isAddingPointMarker)
        {
            QMenu* addMarkerMenu = contextMenu.addMenu("添加标记");
            QAction* addPointMarker = addMarkerMenu->addAction("添加点标记");
            QAction* addCellMarker = addMarkerMenu->addAction("添加面标记");
            QObject::connect(addPointMarker, &QAction::triggered, this, &Graph3DWindowBase::slot_beginAddPointMarker);
            QObject::connect(addCellMarker, &QAction::triggered, this, &Graph3DWindowBase::slot_beginAddCellMarker);
        }
        if (m_isAddingCellMarker || m_isAddingPointMarker)
        {
            QAction* stopAddMarkerMenu = contextMenu.addAction("退出标记模式");
            QObject::connect(stopAddMarkerMenu, &QAction::triggered, this, &Graph3DWindowBase::slot_stopAddMarker);
        }
        
        int selectedRowCount = m_markerDataTable->getInnerTableWidget()->selectedItems().size();
        QAction* deleteChosenMarker = contextMenu.addAction("删除选中标记");
        deleteChosenMarker->setEnabled(selectedRowCount > 0);
        int rowCount = m_markerDataTable->getInnerTableWidget()->rowCount();
        QAction* deleteAllMarkers = contextMenu.addAction("删除所有标记");
        deleteAllMarkers->setEnabled(rowCount > 0);
        QAction* exportTableData = contextMenu.addAction("导出标记数据");
        exportTableData->setEnabled(rowCount > 0);

        QObject::connect(deleteChosenMarker, &QAction::triggered, this, &Graph3DWindowBase::slot_removeChosenMarker);
        QObject::connect(deleteAllMarkers, &QAction::triggered, this, &Graph3DWindowBase::slot_removeAllMarker);
        QObject::connect(exportTableData, &QAction::triggered, this, &Graph3DWindowBase::slot_exportTableData);

        contextMenu.exec(m_qvtkWidget->mapToGlobal(pos));
    }

    void Graph3DWindowBase::slot_exitAddMarkerState()
    {
        if (m_isAddingPointMarker || m_isAddingCellMarker)
        {
            this->slot_stopAddMarker();
        }
    }

    QString Graph3DWindowBase::GetResultDataTypeAsString(int result_type, bool isGainScalar) const
    {
        return getColResultTypeAsStringInner(result_type, isGainScalar);
    }

    int Graph3DWindowBase::getScalarBarType(int fileResultType) const
    {
        if (fileResultType < 3)
        {
            return 0;
        }

        return fileResultType;
    }

    QString Graph3DWindowBase::getColResultTypeAsStringInner(int fileResultType, bool isGainScalar) const
    {
        QString resultDataType;
        switch (fileResultType)
        {
        case 0:
        case 1:
        case 2:
            resultDataType = QObject::tr("电场");
            break;
        case 3:
            resultDataType = QObject::tr("表面场分布");
            break;
        case 4:
            resultDataType = QObject::tr("表面电流密度");
            break;
        case 5:
            resultDataType = QObject::tr("空间电势分布");
            break;
        case 6:
            resultDataType = QObject::tr("表面电势分布");
            break;
        case 7:
            resultDataType = isGainScalar ? QObject::tr("增益") : QObject::tr("电场");
            break;
        case 8:
            resultDataType = QObject::tr("S参数");
            break;
        case 9:
            resultDataType = QObject::tr("驻波比");
            break;
        case 10:
            resultDataType = QObject::tr("电压");
            break;
        case 11:
            resultDataType = QObject::tr("电流");
            break;
        case 12:
            resultDataType = QObject::tr("干扰关联矩阵");
            break;
        case 13:
            resultDataType = QObject::tr("人体吸收比");
            break;
        case 14:
            resultDataType = QObject::tr("附着点路径");
            break;
        case 15:
            resultDataType = QObject::tr("面电势分布");
            break;
        }

        return resultDataType;
    }

    bool Graph3DWindowBase::isContainGeometryPath(const QList<std::tuple<QStringList,
        QObject*, GeomtryActorGenerator*>>& geoTuple, const QStringList& path, 
        std::tuple<QStringList, QObject*, GeomtryActorGenerator*>& firstFoundData) const
    {
        for(const auto& i : geoTuple)
        {
            if(std::get<0>(i) == path)
            {
                firstFoundData = i;
                return true;
            }
        }
        return false;
    }

    bool Graph3DWindowBase::isGeometryPathAllSame(const QList<InfomationGraph3DBasical*>& infos)
    {
        if (infos.size() == 0)
        {
            return false;
        }

        auto firstPath = infos[0]->m_geometryFileName;
        for (const auto& i : infos)
        {
            if (i->m_geometryFileName != firstPath)
            {
                return false;
            }
        }
        return true;
    }

    void Graph3DWindowBase::updateMarkerVisibility()
    {

    }

    void Graph3DWindowBase::updateDataActorPickable()
    {
        for(const auto& info : m_graphInfoList)
        {
            auto dataActor = info->m_actor;
            if(dataActor != nullptr)
            {
                if (m_isAddingPointMarker)
                {
                    if (dataActor->GetMapper()->GetInputAsDataSet()->GetPointData()->GetScalars() != nullptr)
                    {
                        dataActor->SetPickable(true);
                    }
                    else
                    {
                        dataActor->SetPickable(false);
                    }
                }
                else //cell场
                {
                    if (dataActor->GetMapper()->GetInputAsDataSet()->GetCellData()->GetScalars() != nullptr)
                    {
                        dataActor->SetPickable(true);
                    }
                    else
                    {
                        dataActor->SetPickable(false);
                    }
                }
            }
        }
    }


    InfomationGraph3DBasical* Graph3DWindowBase::getInfoFromSidebarNameItem(QListWidgetItem* nameItem)
    {
        for (auto info : m_graphInfoList)
        {
            if (info->m_sidebarItem == nameItem)
            {
                return info;
            }
        }
        return nullptr;
    }

    QList<InfomationGraph3DBasical*> Graph3DWindowBase::getInfoList()
    {
        return m_graphInfoList;
    }

    void Graph3DWindowBase::updateModelDisplayAndClipperButtonCheckState()
    {
        if (m_graphInfoList.isEmpty())
        {
            emit IWidgetSignalInstance->signal_setDisplayModelButtonCheckState(false);
            emit IWidgetSignalInstance->signal_setClipperButtonCheckState(false);
            m_isShowingGeometry = false;
            m_isActiveClip = false;
            return;
        }

        for(const auto& info : m_graphInfoList)
        {
            if (!info->m_geometryFileName.isEmpty())
            {
                return;
            }
        }

        m_isShowingGeometry = false;
        emit IWidgetSignalInstance->signal_setDisplayModelButtonCheckState(false);
    }


    //InfomationGraph3DBasical* Graph3DWindowBase::getInfoFromSidebarSetting(SurfaceElectricFieldMapSidebarData* sidebarData)
    //{
    //    for(auto info : m_graphInfoList)
    //    {
    //        if(info->m_si == sidebarData)
    //        {
    //            return info;
    //        }
    //    }
    //    return nullptr;
    //}

    QString Graph3DWindowBase::generateGraphNameAuto(InfomationGraph3DBasical* info) const
    {
        return QString();
    }

    void Graph3DWindowBase::updateDBAndUnitData()
    {
    }

    void Graph3DWindowBase::updateCubeAxesActor(bool isShow)
    {
        m_isCubeAxesActorVisible = isShow;

        QList<vtkActor*> actorList;
        for (auto i : m_graphInfoList)
        {
            if(i->m_actor==nullptr)
            {
                continue;
            }
            
            if (i->m_actor->GetVisibility() /*|| info->m_dataClipperGenerator->getClipActor()->GetVisibility()*/)
            {
                actorList.append(i->m_actor);
            }
            if (i->m_geomtryActorGenerator != nullptr && i->m_geomtryActorGenerator->getActor()->GetVisibility())//考虑几何
            {
                actorList.append(i->m_geomtryActorGenerator->getActor());
            }
        }

        if (m_cubeAxesActor == nullptr)
        {
            m_cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
            m_cubeAxesActor->SetCamera(m_render3D->GetActiveCamera());
            //m_cubeAxesActor->SetFlyModeToOuterEdges();
            // 只在最靠近相机的三条边上显示（相机位置变化时会跟随更新）
            m_cubeAxesActor->SetFlyMode(vtkCubeAxesActor::VTK_FLY_CLOSEST_TRIAD);
            // 或者固定在当前视角的三条边上，不随相机旋转而改变
            //m_cubeAxesActor->SetFlyMode(vtkCubeAxesActor::VTK_FLY_STATIC_TRIAD);

            //调整标签数目 默认值约为 10.0
            //m_cubeAxesActor->SetScreenSize(30.0); // 标签间隔更大，数量更少
            m_cubeAxesActor->SetScreenSize(10.0);  // 标签更密集

            //m_cubeAxesActor->SetFontFactor(0.1); 
            m_cubeAxesActor->SetXTitle("");
            m_cubeAxesActor->SetYTitle("");
            m_cubeAxesActor->SetZTitle("");

            m_cubeAxesActor->GetXAxesLinesProperty()->SetColor(0.0, 0.0, 1.0); // X 轴边界线设为蓝色
            m_cubeAxesActor->GetYAxesLinesProperty()->SetColor(0.0, 0.0, 1.0); // Y 轴边界线设为蓝色
            m_cubeAxesActor->GetZAxesLinesProperty()->SetColor(0.0, 0.0, 1.0); // Z 轴边界线设为蓝色

            m_cubeAxesActor->GetLabelTextProperty(0)->SetColor(0.0, 0.0, 0.0);
            m_cubeAxesActor->GetLabelTextProperty(1)->SetColor(0.0, 0.0, 0.0);
            m_cubeAxesActor->GetLabelTextProperty(2)->SetColor(0.0, 0.0, 0.0);

            m_cubeAxesActor->GetTitleTextProperty(0)->SetColor(0.0, 0.0, 0.0);
            m_cubeAxesActor->GetTitleTextProperty(1)->SetColor(0.0, 0.0, 0.0);
            m_cubeAxesActor->GetTitleTextProperty(2)->SetColor(0.0, 0.0, 0.0);

            m_cubeAxesActor->GetLabelTextProperty(0)->SetOrientation(90);
            m_cubeAxesActor->GetLabelTextProperty(1)->SetOrientation(90);
            m_cubeAxesActor->GetLabelTextProperty(2)->SetOrientation(90);

            m_cubeAxesActor->Modified();
            m_render3D->AddActor(m_cubeAxesActor);
        }

        m_cubeAxesActor->SetVisibility(m_isCubeAxesActorVisible);
        if (!m_isCubeAxesActorVisible)
        {
            return;
        }

        //计算总边界
        vtkBoundingBox bbox;
        for (vtkActor* actor : actorList)
        {
            if (!actor->GetVisibility())
            {
                continue;
            }
            double b[6];
            actor->GetBounds(b);          // 获取单个 actor 的 bounds
            bbox.AddBounds(b);            // 合并进总 bounding box
        }

        // 提取合并后的 bounds：{xmin, xmax, ymin, ymax, zmin, zmax}
        double globalBounds[6];
        bbox.GetBounds(globalBounds);
        m_cubeAxesActor->SetBounds(globalBounds);

        m_render3D->ResetCameraClippingRange();
        m_renderWindow3D->Render();
    }

    int Graph3DWindowBase::getGraphInfoIDByVtkActor(vtkActor* actor)
    {
        for (auto _graphInfo : m_graphInfoList)
        {
            if (_graphInfo->m_actor == actor)
            {
                return _graphInfo->m_informationID;
            }
        }
        return -1;
    }

    void Graph3DWindowBase::BuildCompleteMarker3DDataInfo(Marker3DDataInfo* MarkerInfo)
    {
        auto _graphInfo = WindowTotalDataInstance->getInfomationGraph3DBasicalByID(MarkerInfo->m_graphInfoID);
        if (_graphInfo == nullptr)
        {
            return;
        }

        vtkDataSet* dataSet = _graphInfo->m_actor->GetMapper()->GetInput();
        double x(0), y(0), z(0);

        vtkSmartPointer<vtkActor> markerActor = nullptr;
        vtkSmartPointer<vtkDistanceToCamera> distanceToCamera = nullptr;
        if (MarkerInfo->m_markerType == 0) // 点拾取
        {
            auto [markerActor1, m_distanceToCamera1, shpere1, fixedGlyph1] = generatePointMarkerActor(MarkerInfo->m_markerPosition[0], MarkerInfo->m_markerPosition[1], MarkerInfo->m_markerPosition[2]);
            markerActor = markerActor1;
            distanceToCamera = m_distanceToCamera1;
        }
        else if (MarkerInfo->m_markerType == 1) // cell拾取
        {
            markerActor = generateCellMarkerActor(dataSet, MarkerInfo->m_pickedId, x, y, z);
        }
        auto captionActor = generateMarkerTextActor(MarkerInfo->m_markerPosition[0], MarkerInfo->m_markerPosition[1], MarkerInfo->m_markerPosition[2], MarkerInfo->m_markerName);

        MarkerInfo->m_markerActor = markerActor;
        MarkerInfo->m_markerTextActor = captionActor;
        MarkerInfo->m_sourceActor = _graphInfo->m_actor;
        MarkerInfo->m_distanceToCamera = distanceToCamera;
        m_marker3DDataInfoList.insert(MarkerInfo->m_markerId, MarkerInfo);
        addAMarkerDataToPickedDataDisplayWindow(MarkerInfo);
        ++m_pickedDataCount;
        m_render3D->AddActor(markerActor);
        m_render3D->AddActor(captionActor);
    }

    void Graph3DWindowBase::writeToProjectFile3DWindowBase(QDomDocument* doc, QDomElement* rootElement)
    {
        GlobalHelperInstance->createDomElement(doc, rootElement, "AnimationType", QString::number(m_animationType));
        GlobalHelperInstance->createDomElement(doc, rootElement, "AnimationSpeed", QString::number(m_animationSpeed));

        m_animationSettingData->writeToProjectFile(doc, rootElement);
        m_animationTooBarInfoData->writeToProjectFile(doc, rootElement);
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsExportingAnimation", QString::number(m_isExportingAnimation));
        GlobalHelperInstance->createDomElement(doc, rootElement, "LoopCount", QString::number(m_loopCount));
        GlobalHelperInstance->createDomElement(doc, rootElement, "ImageSize", QString::number(m_imageSize));
        GlobalHelperInstance->createDomElement(doc, rootElement, "ExportAnimationPath", m_exportAnimationPath);
        GlobalHelperInstance->createDomElement(doc, rootElement, "MarkerNum", QString::number(m_markerNum));
        GlobalHelperInstance->createDomElement(doc, rootElement, "PickedDataCount", QString::number(m_pickedDataCount));
        //GlobalHelperInstance->createDomElement(doc, rootElement, "IsActiveClip", QString::number(m_isActiveClip));
        QString _planNormalDirectionStr = QString::number(m_planeNormalDirection[0]) + "," + QString::number(m_planeNormalDirection[1]) + "," + QString::number(m_planeNormalDirection[2]);
        GlobalHelperInstance->createDomElement(doc, rootElement, "PlaneNormalDirection", _planNormalDirectionStr);
        QString _planeOriginStr = QString::number(m_planeOrigin[0]) + "," + QString::number(m_planeOrigin[1]) + "," + QString::number(m_planeOrigin[2]);
        GlobalHelperInstance->createDomElement(doc, rootElement, "PlaneOrigin", _planeOriginStr);
        GlobalHelperInstance->createDomElement(doc, rootElement, "PlaneInsideOut", QString::number(m_planeInsideOut));
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsAddingPointMarker", QString::number(m_isAddingPointMarker));
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsAddingCellMarker", QString::number(m_isAddingCellMarker));
        //GlobalHelperInstance->createDomElement(doc, rootElement, "IsShowingGeometry", QString::number(m_isShowingGeometry));
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsFirstTimeDataAnimation", QString::number(m_isFirstTimeDataAnimation));
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsBeginExportAnimation", QString::number(m_isBeginExportAnimation));
        GlobalHelperInstance->createDomElement(doc, rootElement, "AnimationCameraAngle", QString::number(m_animationCameraAngle));
        GlobalHelperInstance->createDomElement(doc, rootElement, "AnimationCameraCount", QString::number(m_animationCameraCount));
        GlobalHelperInstance->createDomElement(doc, rootElement, "AnimationDataCount", QString::number(m_animationDataCount));
        GlobalHelperInstance->createDomElement(doc, rootElement, "IsCubeAxesActorVisible", QString::number(m_isCubeAxesActorVisible));


        auto _marker3DDataInfoList = m_marker3DDataInfoList.values();
        for (auto _markerInfo : _marker3DDataInfoList)
        {
            _markerInfo->writeToProjectFile(doc, rootElement);
        }
    }

    void Graph3DWindowBase::readFromProjectFile3DWindowBase(QDomElement* element)
    {
        QDomNodeList AnimationTypeNodeList = element->elementsByTagName("AnimationType");
        m_animationType = AnimationTypeNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationSpeedNodeList = element->elementsByTagName("AnimationSpeed");
        m_animationSpeed = AnimationSpeedNodeList.at(0).toElement().text().toDouble();

        m_animationSettingData->readFromProjectFile(element);
        m_animationTooBarInfoData->readFromProjectFile(element);
        QDomNodeList IsExportingAnimationNodeList = element->elementsByTagName("IsExportingAnimation");
        m_isExportingAnimation = (bool)IsExportingAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList LoopCountNodeList = element->elementsByTagName("LoopCount");
        m_loopCount = LoopCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList ImageSizeNodeList = element->elementsByTagName("ImageSize");
        m_imageSize = ImageSizeNodeList.at(0).toElement().text().toInt();
        QDomNodeList ExportAnimationPathNodeList = element->elementsByTagName("ExportAnimationPath");
        m_exportAnimationPath = ExportAnimationPathNodeList.at(0).toElement().text();
        QDomNodeList MarkerNumNodeList = element->elementsByTagName("MarkerNum");
        m_markerNum = MarkerNumNodeList.at(0).toElement().text().toInt();
        QDomNodeList PickedDataCountNodeList = element->elementsByTagName("PickedDataCount");
        m_pickedDataCount = PickedDataCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsActiveClipNodeList = element->elementsByTagName("IsActiveClip");
        //m_isActiveClip = (bool)IsActiveClipNodeList.at(0).toElement().text().toInt();
        QDomNodeList PlaneNormalDirectionNodeList = element->elementsByTagName("PlaneNormalDirection");
        auto _planeNormalDirectionList = PlaneNormalDirectionNodeList.at(0).toElement().text().split(",");
        m_planeNormalDirection[0] = _planeNormalDirectionList[0].toDouble();
        m_planeNormalDirection[1] = _planeNormalDirectionList[1].toDouble();
        m_planeNormalDirection[2] = _planeNormalDirectionList[2].toDouble();
        QDomNodeList PlaneOriginNodeList = element->elementsByTagName("PlaneOrigin");
        auto _planeOriginList = PlaneOriginNodeList.at(0).toElement().text().split(",");
        m_planeOrigin[0] = _planeOriginList[0].toDouble();
        m_planeOrigin[1] = _planeOriginList[1].toDouble();
        m_planeOrigin[2] = _planeOriginList[2].toDouble();
        QDomNodeList PlaneInsideOutNodeList = element->elementsByTagName("PlaneInsideOut");
        m_planeInsideOut = (bool)PlaneInsideOutNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsAddingPointMarkerNodeList = element->elementsByTagName("IsAddingPointMarker");
        m_isAddingPointMarker = (bool)IsAddingPointMarkerNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsAddingCellMarkerNodeList = element->elementsByTagName("IsAddingCellMarker");
        m_isAddingCellMarker = (bool)IsAddingCellMarkerNodeList.at(0).toElement().text().toInt();
        //QDomNodeList IsShowingGeometryNodeList = element->elementsByTagName("IsShowingGeometry");
        //m_isShowingGeometry = (bool)IsShowingGeometryNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsFirstTimeDataAnimationNodeList = element->elementsByTagName("IsFirstTimeDataAnimation");
        m_isFirstTimeDataAnimation = (bool)IsFirstTimeDataAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsBeginExportAnimationNodeList = element->elementsByTagName("IsBeginExportAnimation");
        m_isBeginExportAnimation = (bool)IsBeginExportAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationCameraAngleNodeList = element->elementsByTagName("AnimationCameraAngle");
        m_animationCameraAngle = AnimationCameraAngleNodeList.at(0).toElement().text().toDouble();
        QDomNodeList AnimationCameraCountNodeList = element->elementsByTagName("AnimationCameraCount");
        m_animationCameraCount = AnimationCameraCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationDataCountNodeList = element->elementsByTagName("AnimationDataCount");
        m_animationDataCount = AnimationDataCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsCubeAxesActorVisibleNodeList = element->elementsByTagName("IsCubeAxesActorVisible");
        m_isCubeAxesActorVisible = (bool)IsCubeAxesActorVisibleNodeList.at(0).toElement().text().toInt();

        auto _Marker3DDataInfoNodeList = element->elementsByTagName("Marker3DDataInfo");
        auto _MarkerInfosCount = _Marker3DDataInfoNodeList.count();
        if (_MarkerInfosCount == 0)
            return;
        for (int i = 0; i < _MarkerInfosCount; i++)
        {
            Marker3DDataInfo* Marker3D = new Marker3DDataInfo(this);
            QDomElement elemModel = _Marker3DDataInfoNodeList.at(i).toElement();

            Marker3D->readFromProjectFile(&elemModel);
            BuildCompleteMarker3DDataInfo(Marker3D);
        }
    }
} // namespace pst
